package com.gframework.boot.mvc.controller.protocal.basic;

import java.io.Serializable;

import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;

import com.gframework.boot.mvc.controller.errcode.ErrorCodeException;

/**
 * restful风格api后端向前端发送数据封装类.
 * <p>
 * 本类封装数据格式是一个最为基本的格式，只包含了status（状态码）、message（状态描述）和 data（返回数据）。
 * <p>
 * 如果你想要实现一个文件下载或者浏览器预览的功能，你可以返回 {@link FileResponse}类对象来实现此操作。
 * <p>
 * 你可以单独使用本类进行其他业务的处理，但本类的主要功能还是和{@link BasicProtocalControllerAOP}
 * 控制层代理类进行配合使用。
 * 统一控制层方法返回数据的规范性。你也可以直接手动构造本类或本类的子类对象去返回。
 * <p>
 * 本类不支持克隆和序列化，如果有类似需求，请转化为json字符串进行处理。
 * 
 * @since 1.0.0
 * @author Ghwolf
 * @see FileResponse
 * @see BasicProtocalControllerAOP
 * @see ErrorCodeException
 */
@SuppressWarnings("serial")
public class ServerResponse implements Serializable{
	/**
	 * 操作状态号.
	 * 
	 * <pre>
	 * 此状态号表示的是业务状态号，如果状态号是http标准状态号，通常也会让响应状态号与之相同。
	 * 不过为了更好的表达语义，最好使用http的标准状态好，来减少客户端的开发复杂度并降低学习成本。
	 * 基础状态号可以参考spring的枚举类 {@link HttpStatus}
	 * </pre>
	 * 
	 * <strong>默认是：200</strong>
	 * 
	 * @see HttpStatus
	 */
	private int status = HttpStatus.OK.value();
	/**
	 * 状态描述信息.
	 * <p>
	 * 此字段表示的是当前状态的一个描述信息，你需要注意以下几点：
	 * <ol>
	 * <li>此内容可以为null，如果为null，则会按照默认的空字符串处理</li>
	 * <li>描述信息需要考虑一个语言环境问题，即国家化问题。</li>
	 * <li>通常来说你可以将异常描述信息封装在异常类中，那样一来就可以更好的获取到。</li>
	 * <li>除非特定的协议号，否则不建议针对每个协议号写死一种描述信息，这样会给人造成很大的疑惑。</li>
	 * <li>描述信息应当简洁清楚明了，但也不能暴漏过多后台信息，前段是有可能输出这个信息的。</li>
	 * </ol>
	 * </p>
	 */
	@Nullable
	private String message;
	/**
	 * 具体返回的数据.
	 * <p>
	 * 此字段保存的就是具体返回的数据内容。允许为null，这样一来则表示只返回操作状态，无具体内容。
	 */
	@Nullable
	private Object data;

	/**
	 * 构造一个空的本类对象
	 */
	public ServerResponse() {
	}

	/**
	 * 构造一个具有指定状态和描述的本类对象
	 * 
	 * @param status 操作状态号 {@link #status}
	 * @param message 描述信息 {@link #message}
	 */
	public ServerResponse(int status, @Nullable String message) {
		this(status, message, null);
	}

	/**
	 * 构造一个完整的本类对象，包含状态，描述和数据
	 * 
	 * @param status 操作状态号 {@link #status}
	 * @param message 描述信息 {@link #message}
	 * @param data 返回数据 {@link #data}
	 */
	public ServerResponse(int status, @Nullable String message, @Nullable Object data) {
		this.status = status;
		this.message = message;
		this.data = data;
	}

	/**
	 * 取得协议状态码.
	 * <p>
	 * 通过此方法返回的结果一定不会是负数，如果你设置了status为负数，则会按照错误的500状态码进行处理。
	 * </p>
	 * 状态码可以参考 {@link HttpStatus} 枚举类。
	 * 
	 * @return 返回状态码
	 * @see HttpStatus
	 */
	public int getStatus() {
		return this.status < 0 ? HttpStatus.INTERNAL_SERVER_ERROR.value() : this.status;
	}

	/**
	 * 设置协议状态码.
	 * <p>
	 * 通过此方法可以设置本次操作的状态码。你可以将实现一个继承了 {@link ErrorCodeException}的异常类，
	 * 此类提供了一个获取状态码的方法，可以使得你方便的获取状态码。但是不推荐在service层及更底层过多的抛出此类异常的子类，
	 * 因为service是不需要关注返回什么状态的，所以通常这些由controller来抛出，但是可以规定一些语义足够清晰的异常类来继承这个类，
	 * 让业务层来使用。
	 * </p>
	 * 状态码可以参考 {@link HttpStatus} 枚举类。
	 * 
	 * @param status 要设置的状态码
	 * @see HttpStatus
	 */
	public void setStatus(int status) {
		this.status = status;
	}

	/**
	 * 设置状态描信息.
	 * <p>
	 * 此字段表示的是当前状态的一个描述信息，你需要注意以下几点：
	 * <ol>
	 * <li>此内容可以为null，如果为null，则会按照默认的空字符串处理</li>
	 * <li>描述信息需要考虑一个语言环境问题，即国家化问题。</li>
	 * <li>通常来说你可以将异常描述信息封装在异常类中，那样一来就可以更好的获取到。</li>
	 * <li>除非特定的协议号，否则不建议针对每个协议号写死一种描述信息，这样会给人造成很大的疑惑。</li>
	 * <li>描述信息应当简洁清楚明了，但也不能暴漏过多后台信息，前段是有可能输出这个信息的。</li>
	 * </ol>
	 * </p>
	 * 
	 * @param message 描述信息
	 */
	public void setMessage(@Nullable String message) {
		this.message = message;
	}

	/**
	 * 取得状态描述信息.
	 * 
	 * @return 返回状态描述信息
	 */
	public String getMessage() {
		return this.message == null ? "" : this.message;
	}

	/**
	 * 设置返回的具体数据.
	 * <p>
	 * 设置的数据可以是任意类型的。
	 * </p>
	 * 
	 * @param data 设置响应数据
	 */
	public void setData(@Nullable Object data) {
		this.data = data;
	}

	/**
	 * 取得返回的具体数据.
	 * 
	 * @return 取得响应数据
	 */
	@Nullable
	public Object getData() {
		return this.data;
	}
	
	/**
	 * 构造一个【不可修改的】完整的本类对象，包含状态，描述和数据
	 * @param status 操作状态号 {@link #status}
	 * @param message 描述信息 {@link #message}
	 * @param data 返回数据 {@link #data}
	 */
	public static ServerResponse unmodifiableBody(int status, String message, Object data) {
		return new UnmodifiableServerResponse(status,message,data);
	}
	
	/**
	 * 构造一个【不可修改的】完整的本类对象，包含状态，描述
	 * @param status 操作状态号 {@link #status}
	 * @param message 描述信息 {@link #message}
	 */
	public static ServerResponse unmodifiableBody(int status, String message) {
		return new UnmodifiableServerResponse(status,message);
	}

	/**
	 * 一个不可修改的ServerResponse类对象
	 * 
	 * @author Ghwolf
	 */
	static class UnmodifiableServerResponse extends ServerResponse {

		public UnmodifiableServerResponse(int status, String message, Object data) {
			super(status, message, data);
		}

		public UnmodifiableServerResponse(int status, String message) {
			super(status, message);
		}

		@Override
		public void setData(Object data) {
			// ignore
		}

		@Override
		public void setMessage(String message) {
			// ignore
		}

		@Override
		public void setStatus(int status) {
			// ignore
		}
	}
	
}
